
/*****************************************************************************\
 *                Test for the collision pipeline
\*****************************************************************************/

/*! @file
 *
 *  @brief
 *    Move a number of objects around inside a cube.
 *
 *  This is an example program showing how to use the collision detection API.
 *  (This is similar to the original movem from Y.)
 *  The user can rotate / translate / zoom the cage.
 *  Only the center of objects will be checked against the "cage".
 *
 *  Type './movem -h' for an explanation of the command line options and keys.
 *
 *  @author Weiyu Yi 
 *
 *  @bugs
 *
 *  @flags
 *
 */

//---------------------------------------------------------------------------
//  Includes
//---------------------------------------------------------------------------

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>

#if defined(__APPLE__)
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include <Collision.h>
#include <Collision.h>
#include <ColBoxtree.h>
#include <ColDopTree.h>
#include <ColTopology.h>
#include <ColUtils.h>

#include <osg/ref_ptr>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/StateSet>
#include <osg/Math>
#include <osg/StateAttribute>
#include <osg/PolygonMode>
#include <osg/Transform>
#include <osg/Drawable>
#include <osg/Shape>
#include <osg/ShapeDrawable>
#include <osg/PrimitiveSet>
#include <osgViewer/Viewer>
#include <osgGA/TrackballManipulator>

#include <ColWrapperOpenSceneGraph.h>
#include <ColUtilsOpenSceneGraph.h>

#ifdef _WIN32
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#endif

using col::ColGeometry;
using col::Primitive;
using col::Boxtree;
using col::DopTree;

using namespace osg;

class CollisionSyncFun; //Needed to synch during concurrent collision detection


/*-----------------------------------------------------------------------------
 *  Global Variables
 *-----------------------------------------------------------------------------*/
namespace
{

// render options
bool with_window = true;
bool animation_started = false;

bool moved = true;
std::vector<const std::vector<col::Point3> *> fixed_points;
std::vector<const std::vector<col::Point3> *> moving_points;

// Col. variables
col::AlgoE algo = col::ALGO_DOPTREE;
col::CollisionPipeline* pipeline;
ref_ptr<osg::Group> root;
ref_ptr<osg::Geode> cage;

// render variables
osgViewer::Viewer viewer;

// options
const unsigned int 	MaxNumObjs = 500;

unsigned int	nmovingobjs = 10;
unsigned int	complexity = 10;
double			cage_radius = 10;
const Vec4f		cage_color( 0.0, 0.8, 0.8, 0 );
bool			concurrent_coldet = false;
col::LevelOfDetectionE	level_of_det = col::LEVEL_EXACT;
bool            all_polygons = false;
bool            with_grid = false;
unsigned int	gridsize = 10;
unsigned int	NLoops = 1;
volatile bool   exitPipeline = false;

// multithreading support
boost::thread* pt;
boost::mutex callback_mutex;
boost::barrier sync_barrier(2);

// state
unsigned int		ncollision = 0;
unsigned int		cur_frame_counter = 0;

// pseudo-random number generator (instead of drand48)
col::FibRand *      prng;

}

/***************************************************************************\
 * MovingObj
\***************************************************************************/

struct MovingObj
{
	// ctor, dtor,
	MovingObj();

	// methods
	void setRandomPos( void );
	void setRandomVel( void );
	void makeCollidable( void );
	void registerTransformation( void );
	void setHit( const Vec3f &colnormal );
	void updateTransformation( void );
	void newVelocity( void );

	// state
	col::ColID			m_objID;
	bool				m_coll;
	ref_ptr<Group>		m_node;
	Matrixf				m_mat;
	Vec3f				m_transl_vel;	// translational velocity
	double				m_transl_scale;
	Matrixf				m_rot_vel;		// rotational velocity, derived from ...
	Vec3f				m_rot_axis;
	double				m_rot_angle;
	unsigned int		m_hit;			// coll in current frame if m_hit == cur_frame_counter
	Vec3f				m_collision_normal;	// will be used to reflect velocity
};


MovingObj mobj[MaxNumObjs];

MovingObj::MovingObj()
:
	m_node(),
	m_mat(),
	m_transl_vel(),
	m_rot_vel(),
	m_rot_axis(),
	m_rot_angle(),
	m_hit(0),
	m_coll(false),
	m_collision_normal()
{
	// initial m_node
	m_node = new osg::MatrixTransform;
	ref_ptr<MatrixTransform> mt = dynamic_cast<MatrixTransform*>( m_node.get() );
	m_mat.makeIdentity();
	mt->setMatrix( m_mat );
}

/** 
 * @brief
 *   register the geometry of current moving object to col. pipeline
 */
void MovingObj::makeCollidable( void )
{
	ref_ptr<Geode> geode = dynamic_cast<Geode*>( m_node->getChild( 0 ) );
	ref_ptr<Geometry> geom = dynamic_cast<Geometry*>( geode->getDrawable( 0 ) );
	m_objID = pipeline->makeCollidable( static_cast<void *>( geom.get() ), col::convertGeometryFromOpenSceneGraph );
}

/** 
 * @brief 
 *   register the transformation matrix to col. pipeline
 */
void MovingObj::registerTransformation( void )
{
	ref_ptr<osg::MatrixTransform> mt = dynamic_cast<osg::MatrixTransform*>( m_node.get() );
	m_mat = mt->getMatrix();
	pipeline->moveObject( m_objID, static_cast<void *>(&m_mat), col::convertMatrixFromOpenSceneGraph );
}

/** 
 * @brief
 *   save the collison normal 
 * 
 * @param colnormal the length should be reserved in order to compute the velocity scale
 */
void MovingObj::setHit( const Vec3f &colnormal )
{
	m_hit = cur_frame_counter;			// does this work also with concurrent coll.det.?
	m_collision_normal = colnormal;
}

/** 
 * @brief 
 *   move and update the moving object one frame ahead
 */
void MovingObj::updateTransformation( void )
{
	ref_ptr<osg::MatrixTransform> mt = dynamic_cast<osg::MatrixTransform*>( m_node.get() );
	m_mat = mt->getMatrix();

	for ( unsigned int i = 0; i < 3; i ++ )
		m_mat( 3, i ) += m_transl_vel[i] * m_transl_scale;

	// m_mat = m_mat * m_mj->m_rot_vel;
	// be care!
	// openscenegraph, the matrix is organized as rows
	// osg::Matrix = opengl::Matrix->transport()
	m_mat = m_rot_vel * m_mat;

	mt->setMatrix( m_mat );

	Vec3f colnormal(0.0,0.0,0.0);
	for ( unsigned int i = 0; i < 3; i ++ )
	{
		if ( m_mat( 3, i ) > cage_radius )
			colnormal[i] = -1;
		else
		if ( m_mat( 3, i ) < -cage_radius )
			colnormal[i] =  1;
	}

	if ( col::NearZero < colnormal.length() )
	{
		colnormal.normalize();
		setHit( colnormal );
	}
}

/** 
 * @brief 
 *   caculate the new velocity if collision has been detected upon the current moving obj
 */
void MovingObj::newVelocity( void )
{
	if ( m_hit == cur_frame_counter ) // new velocity
	{
		// new velocity length
		// m_transl_scale is greater if m_collision_normal.length() is smaller
		m_transl_scale = 0.1 / m_collision_normal.length();

		// new velocity direction
		m_collision_normal.normalize();
		m_transl_vel += m_collision_normal;
		m_transl_vel.normalize();

		// invert rotate velocities
		m_rot_angle = -m_rot_angle;
		m_rot_vel.makeRotate( m_rot_angle, m_rot_axis );
	}
}

/** 
 * @brief 
 *   initialize random position
 */
void MovingObj::setRandomPos( void )
{
	Vec3f randpnt( (prng->frand()-0.5) * cage_radius,
						(prng->frand()-0.5) * cage_radius,
						(prng->frand()-0.5) * cage_radius);

	ref_ptr<osg::MatrixTransform> mt = dynamic_cast<osg::MatrixTransform*> ( m_node.get() );
	Matrixf mat = mt->getMatrix();
	mat.makeTranslate( randpnt[0], randpnt[1], randpnt[2] );
	mt->setMatrix( mat );
}

/** 
 * @brief 
 *   initialize random velocity
 */
void MovingObj::setRandomVel( void )
{
	// translation velocity
	m_transl_scale = prng->frand();
	m_transl_vel = Vec3f( prng->frand()-0.5, prng->frand()-0.5, prng->frand()-0.5 );
	m_transl_vel.normalize();
	m_transl_vel *= m_transl_scale;

	// rotation velocity
	m_rot_axis = Vec3f( prng->frand()-0.5, prng->frand()-0.5, prng->frand()-0.5 );
	m_rot_axis.normalize();
	m_rot_angle = prng->frand() * 0.2;
	m_rot_vel.makeRotate( m_rot_angle, m_rot_axis );
}

/***************************************************************************\
 * Scene
\***************************************************************************/

/** 
 * @brief 
 *   update the scenegraph and register the change to col. pipeline
 */
void updateScene( void )
{
	for ( unsigned int i=0; i<nmovingobjs; i++ )
	{
		mobj[i].registerTransformation();
		mobj[i].updateTransformation();
		mobj[i].newVelocity();
	}
}

/** 
 * @brief 
 *   create the scenegraph
 */
void createScene()
{
	// create root
	root = new Group;

	// create cage
	cage = new Geode;
	ref_ptr<Geometry> cage_geom = new Geometry;
	col::makeCube( cage_geom.get(), cage_radius, true );
	ref_ptr<Vec4Array> cage_colors = new Vec4Array();	
	cage_colors->push_back( cage_color );
	cage_colors->push_back( cage_color );
	cage_colors->push_back( cage_color );
	cage_colors->push_back( cage_color );
	cage_colors->push_back( cage_color );
	cage_colors->push_back( cage_color );
	cage_geom->setColorArray( cage_colors.get() );
	cage_geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
	
	ref_ptr<Geode> cage_geode = dynamic_cast<Geode*>( cage.get() );
	cage_geode->addDrawable( cage_geom.get() );
	cage->setDataVariance( osg::Object::STATIC );

	root->addChild( cage.get() );

	// create moving objs
	double rad = 0.7;
	unsigned int type = 0;

	for ( unsigned int i=0; i<nmovingobjs; i++ )
	{
		type = type % 3;

		switch (type)
		{
			case 0: // Cube
			{
				// matrix
				mobj[i].setRandomPos();
				mobj[i].setRandomVel();

				// geo
				ref_ptr<Geometry> geom = new Geometry;
				col::makeCube( geom.get(), rad );
				ref_ptr<Geode> geode = new Geode;
				geode->addDrawable( geom.get() );
				mobj[i].m_node->addChild( geode.get() );

				// color
				ref_ptr<Vec4Array> colors = new Vec4Array();	
				Vec4f color = Vec4f( prng->frand(), prng->frand(), prng->frand(), 0 );
				colors->push_back( color );
				geom->setColorArray( colors.get() );
				geom->setColorBinding(osg::Geometry::BIND_OVERALL);

				break;
			}
			case 1: // Sphere
			{
				// matrix
				mobj[i].setRandomPos();
				mobj[i].setRandomVel();

				// geo
				ref_ptr<Geometry> geom = new Geometry;
				col::makeSphere( geom.get(), 2, rad );
				ref_ptr<Geode> geode = new Geode;
				geode->addDrawable( geom.get() );
				mobj[i].m_node->addChild( geode.get() );

				// color
				ref_ptr<Vec4Array> colors = new Vec4Array();	
				Vec4f color = Vec4f( prng->frand(), prng->frand(), prng->frand(), 0 );
				colors->push_back( color );
				geom->setColorArray( colors.get() );
				geom->setColorBinding(osg::Geometry::BIND_OVERALL);

				break;
			}
			case 2: // Plane
			{
				// matrix
				mobj[i].setRandomPos();
				mobj[i].setRandomVel();

				// geo
				ref_ptr<Geometry> geom = new Geometry;

				col::makePlane( geom.get(), 2*rad, 2*rad, 5, 5 );
				ref_ptr<Vec3Array> vertices = dynamic_cast<Vec3Array*>( geom->getVertexArray() );
				vertices->at( 0 )[2] += 0.3;
				vertices->at( vertices->size()-1 )[2] -= 0.3;

				ref_ptr<Geode> geode = new Geode;
				geode->addDrawable( geom.get() );
				mobj[i].m_node->addChild( geode.get() );

				// color
				ref_ptr<Vec4Array> colors = new Vec4Array();	
				Vec4f color = Vec4f( prng->frand(), prng->frand(), prng->frand(), 0 );
				colors->push_back( color );
				geom->setColorArray( colors.get() );
				geom->setColorBinding(osg::Geometry::BIND_OVERALL);

				break;
			}
			default:
				fprintf( stderr, "the programm shouldn't arrive here\n" );
		}

		// set data variance for each group
		//mobj[i].m_node->setDataVariance( osg::Object::STATIC );
		//mobj[i].m_node->setDataVariance( osg::Object::DYNAMIC );
		//mobj[i].m_node->setUpdateCallback( new MovingRenderCB( &mobj[i] ) );

		// add to root
		root->addChild( mobj[i].m_node.get() );

		//
		type ++;
	}

	// set root state attribute
	ref_ptr<osg::StateSet> state = root->getOrCreateStateSet();
	ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
	state->setAttributeAndModes( pm.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
}

/***************************************************************************\
 * MovemCallback
\***************************************************************************/

class MovemCallback : public col::Callback
{
public:
	MovemCallback( MovingObj &obj1, MovingObj &obj2,
				   col::LevelOfDetectionE level );
	virtual ~MovemCallback();
	virtual void operator () (const col::Data *data) throw ();
	static void addCallback( MovingObj &obj1, MovingObj &obj2,
							 col::LevelOfDetectionE level );
protected:
	MovingObj & mobj1;
	MovingObj & mobj2;
};

MovemCallback::MovemCallback( MovingObj &inobj1, MovingObj &inobj2,
							  col::LevelOfDetectionE inlevel )
:	col::Callback(inobj1.m_objID, inobj2.m_objID, false, all_polygons, inlevel),
	mobj1(inobj1), mobj2(inobj2)
{
}


MovemCallback::~MovemCallback() {}


void MovemCallback::operator () (const col::Data *) throw ()
{
	if ( mobj1.m_objID != obj1ID || mobj2.m_objID != obj2ID  )
	{
		fputs("MovemCallback: mobj.node != obj !\n",stderr);
		return;
	}

	printf("Callback: %d <-> %d\n", mobj1.m_objID, mobj2.m_objID);
	
	callback_mutex.lock();

	Matrixf mat1 = dynamic_cast<MatrixTransform*>( mobj1.m_node.get() )->getMatrix();
	Matrixf mat2 = dynamic_cast<MatrixTransform*>( mobj2.m_node.get() )->getMatrix();

	Vec3f pos1 ( mat1( 3, 0 ), mat1( 3, 1 ), mat1( 3, 2 ) );
	Vec3f pos2 ( mat2( 3, 0 ), mat2( 3, 1 ), mat2( 3, 2 ) );
	Vec3f collnormal( pos1 - pos2 );

	// collnormal isn't normalized
	// the length is take into account to compute the velocity's scale value
	mobj1.setHit( collnormal );
	mobj2.setHit( -collnormal );

	callback_mutex.unlock();

	ncollision ++ ;
}


void MovemCallback::addCallback( MovingObj &obj1, MovingObj &obj2,
								 col::LevelOfDetectionE level )
{
    pipeline->addCallback( new MovemCallback( obj1, obj2, level ) );
}


/***************************************************************************\
 * SyncFun
\***************************************************************************/

class CollisionSyncFun : public col::SyncFun
{
public:
	CollisionSyncFun( );
	virtual ~CollisionSyncFun();
	virtual bool operator () () throw ();
};

CollisionSyncFun::CollisionSyncFun( )
{
}

CollisionSyncFun::~CollisionSyncFun()
{
}

//Here you can implement synchronization between the collision pipeline
//and your own (main) thread. In this example it isn't much.
//You MUST implement the synchronization (i.e., exchange of changelists) somewhere!
//and this sync function is a good place to do this.
bool CollisionSyncFun::operator () ( ) throw ()
{
	if ( exitPipeline )
	{
		fprintf(stdout, "Collision thread: received exit signal.\n");
		return false;
	}

	if( cur_frame_counter > 0 )
	{
		// wait for the end of the rendering of the last frame of the scenegraph
		sync_barrier.wait();

		// effect the scenegraph with the result of the coll::check()
		// synchronizing data between 2 threads
		//
		// calc "collision normal"
		// ----------

		// debug...
		printf( "\t\t\t\tsynchronizing...\n" );

		sync_barrier.wait();
		// proceed check-loop thread, at the same time the render thread too
	
		// debug...
		printf( "\t\t\t\tcheck loop\n" );
	}
	cur_frame_counter ++ ;
	return true;
}


/***************************************************************************\
 *                       Check and  process coll                           *
\***************************************************************************/

void coll_check( void )
{
	cur_frame_counter ++;	
	pipeline->check();
}

void movemExit( void )
{
	printf("num collisions = %u\n", ncollision );
    if( concurrent_coldet )
    {
        exitPipeline = true;
		//pt->join();
    }
	delete pipeline;
}


/***************************************************************************\
 *                              Main                                       *
\***************************************************************************/

void commandlineerror( char *cmd, char *parm )
{
	if ( cmd )
		fprintf(stderr, "Offending option: %s\n", cmd );
	if ( parm )
		fprintf(stderr, "with first parameter: %s\n", parm );

	fprintf(stderr, "\n\nUsage: movem options ...\n"
	"Options:\n"
	"<space>     start moving\n"
	"-n num obj  number of moving objects\n"
	"-g obj      geometry type (default = all different kinds)\n"
	"              obj = pl, sh, to\n"
	"-u          coll. det. module runs concurrently\n"
	"-C          level of detection = convex hull only\n"
    "-a          find all colliding polygons\n"
	"-A algo     tell col. which algo should be used\n"
	"			   algo = bx, dp\n"
	"-W #loops   without window; perform #loops many cycles, then exit\n"
    "-G gridsize Use grid with dimension gridsize per axis\n"
	"-h          this help menu\n"
	"Keys:\n"
	"i           switch drawing mode (filled/wireframe/point)\n"
	"p           switch projection mode\n"
	"ESC         quit\n"
	"\n");

	if ( cmd )
		exit(-1);				// problem occured
	else
		exit(0);				// was option -h
}


void parsecommandline( int argc, char *argv[] )
{
	/* valid option characters; last char MUST be 0 ! */
	char optionchar[] =   { 'h', 'g', 'u', 'A', 'C', 'a', 'n', 'W', 'G', '\0' };
	int musthaveparam[] = {  0 ,  1,   0,   1,   0,   0,   1,   1,   1  };
	int mhp[256];
	int isopt[256];
	char optchar;


	unsigned int nopts = strlen(optionchar);
	if ( nopts > 50 )
	{
		fprintf(stderr, "\n\nparsecommandline: the option-chars string "
				"seems to be\nVERY long (%d bytes) !\n\n", nopts );
		exit(-1);
	}

	fill_n( isopt, 256, 0 );
	fill_n( mhp, 256, 0 );
	for ( unsigned int i = 0; i < nopts; i ++ )
	{
		if ( isopt[static_cast<int>(optionchar[i])] )
		{
			fprintf(stderr, "\n\nparsecommandline: Bug: option character '%c'"
					" is specified twice in the\n"
							"option character array !\n\n", optionchar[i] );
			exit(-1);
		}
		isopt[ static_cast<int>(optionchar[i]) ] = 1;
		mhp[ static_cast<int>(optionchar[i])] = musthaveparam[i];
	}

	++argv; --argc;
	while ( argc > 0 )
	{
		if ( argv[0][0] == '-' )
		{
			optchar = argv[0][1];

			if ( ! isopt[static_cast<int>(optchar)] )
			{
				fprintf(stderr, "\nIs not a valid command line option\n");
				commandlineerror( argv[0], NULL );
			}
			for ( int i = 0; i < mhp[static_cast<int>(optchar)]; i ++ )
				if ( ! argv[1+i] || argv[1+i][0] == '-' )
				{
					fprintf(stderr, "\nCommand line option -%c must "
							"have %d parameter(s)\n",
							argv[0][1], mhp[static_cast<int>(optchar)] );
					commandlineerror( argv[0], NULL );
					argv += 1 + i;
					argc -= 1 + i;
					continue;
				}

			switch ( optchar )
			{
				case 'h': commandlineerror( NULL, NULL);  break;
				case 'u': concurrent_coldet = true; break;
				case 'C': level_of_det = col::LEVEL_HULL; break;
				case 'W': with_window = false;
						  NLoops = atoi( argv[1] );
						  break;
				case 'n': nmovingobjs = atoi( argv[1] ); break;
                case 'a': all_polygons = true; break;
				case 'A': if ( ! strcmp(argv[1],"bx") )
							  algo = col::ALGO_BOXTREE;
						  else
						  if ( ! strcmp(argv[1],"dp") )
							  algo = col::ALGO_DOPTREE;
						  break;
				case 'G': with_grid = true;
						  gridsize = atoi( argv[1] );
						  break;

				default: fprintf(stderr, "\nBug in parsecommandline !\n");
						 commandlineerror( argv[0], NULL );
			}

			argv += 1 + mhp[static_cast<int>(optchar)];
			argc -= 1 + mhp[static_cast<int>(optchar)];
		}
		else
		{
			/* command line arg doesn't start with '-' */
			fprintf(stderr, "\nThis is not a valid command line option\n");
			commandlineerror( argv[0], NULL );
			/* or, load file instead .. */
		}
	}

	if ( nmovingobjs > MaxNumObjs )
	{
		fprintf(stderr,"number of moving objects (%u) > %d!\n",
				nmovingobjs, MaxNumObjs );
		exit(-1);
	}
}

int main(int argc, char** argv)
{

	// initialize global and static data
    prng = new col::FibRand( time(NULL) );
	
	// parse command line options
	parsecommandline( argc, argv );

	// initialize scene
	createScene();
	viewer.setSceneData( root.get() );
	viewer.setCameraManipulator( new osgGA::TrackballManipulator );

	try
	{
		// this should be done before makeCollidable() or addCallback()!
		pipeline = new col::CollisionPipeline( "CollisionPipelineThread"/*ThreadName*/, 1 /*ThreadID*/);

  		pipeline->setUseHulls( true );
		if( with_grid )
		{
			unsigned int size[3] = { gridsize, gridsize, gridsize };
			REAL min[3] = { -cage_radius, -cage_radius, -cage_radius };
			REAL max[3] = { cage_radius, cage_radius, cage_radius };
			pipeline->useGrid( size, min, max );
		}

		// register objects with collision detection module
		for ( unsigned int i = 0; i < nmovingobjs; i ++ )
			mobj[i].makeCollidable();
		printf("register obj complete\n");
		// register callbacks
		for ( unsigned int i = 0; i < nmovingobjs; i ++ )
			for ( unsigned int j = 0; j < i; j ++ )
				MovemCallback::addCallback( mobj[i], mobj[j], level_of_det );
		printf("register callbacks complete\n");
		// register the transformation
		for ( unsigned int i = 0; i < nmovingobjs; i ++ )
			mobj[i].registerTransformation();
		printf("register transformation complete\n");

		if ( concurrent_coldet )
		{
			CollisionSyncFun *syncfun = new CollisionSyncFun();
			pipeline->setSyncFun( syncfun );

			pt = pipeline->runConcurrently( NULL );
			if ( pt!=NULL )
				printf("\t\t\t\tpipeline thread starts\n");
		}
	}
	catch ( col::XCollision &x )
	{
		fputs("movem: collision exception!\n",stderr);
		x.print();
		exit(-1);				// in a real app, we would try to continue
	}

	// run...
	if ( with_window )
	{
		if ( concurrent_coldet )
		{
			while( ! viewer.done() )
			{

				sync_barrier.wait();

				// sychronizing...

				// debug...
				printf( "sychronizing...\n" );

				sync_barrier.wait();
				//then update and render the scenegraph

				// debug...
				printf( "update scene...\n" );
				callback_mutex.lock();
				updateScene();
				callback_mutex.unlock();

				viewer.frame();
			}
		}
		else
		{
			while( ! viewer.done() )
			{
				coll_check();

				updateScene();

				viewer.frame();
			}
			//viewer.run();
		}
    }
	else
	{
		if ( concurrent_coldet )
		{
			for ( unsigned int i = 0; i < NLoops; i ++ )
			{
				sync_barrier.wait();

				// sychronizing...

				// debug...
				printf( "sychronizing...\n" );

				sync_barrier.wait();
				//then update and render the scenegraph

				// debug...
				printf( "update scene...\n" );
				callback_mutex.lock();
				updateScene();
				callback_mutex.unlock();
			}
		}
		else
		{
			for ( unsigned int i = 0; i < NLoops; i ++ )
			{
				coll_check();

				updateScene();
			}
		}
		movemExit();
	}

	return 0;
}

